socketpair 使用解析

11/15/2023

socketpair 在 Framework 的 Native 层中有大量的使用,接下来我们对 socketpair 做基本的了解。

socket 基于 c/s 模型,需要客户端与服务端进行连接,连接还需要知道服务端的 ip,如果是 unix domain socket ,则需要对应的文件路径。两端连接后可以实现服务端和客户端的双向通信,这种情况一般比较适合于很多个客户端对应一个服务的情况。但是如果说本身就只是单独 2 个进程进行相互通信的需求,我们就可以使用 socketpair api,对于 socketpair ,不需要 connect,也不需要 ip 或者路径,它是匿名的,在 socketpair 初始化时就已经确定了 2 个对应的 socket 的 fd,直接读写这两个 fd 即可完成通信。

接下来我们来看具体的 API:

#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int d, int type, int protocol, int sv[2])
1
2
3
  • socketpair() 函数用于创建一对无名的、相互连接的套接字
  • 如果函数成功,则返回 0,创建好的套接字分别是 sv[0] 和 sv[1];否则返回 -1,错误码保存于 errno 中。

我们可以直接读写这两个套接字来进行通信,在使用过程中有以下几个注意点:

  1. 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读;
  2. 如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功;
  3. 读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。

接下来看两个简单的 demo:

// 同进程
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <error.h> 
#include <errno.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
 
const char* str = "SOCKET PAIR TEST.";
 
int main(int argc, char* argv[]){
    char buf[128] = {0};
    int socket_pair[2]; 
    pid_t pid; 
 
    if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) { 
        printf("Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
        return EXIT_FAILURE; 
    } 
 
    int size = write(socket_pair[0], str, strlen(str));
    //可以读取成功;
    read(socket_pair[1], buf, size);
    printf("Read result: %s\n",buf);
    return EXIT_SUCCESS;    
} 

//不同进程
#include <stdio.h> 
#include <string.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <error.h> 
#include <errno.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
 
const char* str = "SOCKET PAIR TEST.";
 
int main(int argc, char* argv[]){
    char buf[128] = {0};
    int socket_pair[2]; 
    pid_t pid; 
 
    if(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair) == -1 ) { 
        printf("Error, socketpair create failed, errno(%d): %s\n", errno, strerror(errno));
        return EXIT_FAILURE; 
    } 
 
    pid = fork();
    if(pid < 0) {
        printf("Error, fork failed, errno(%d): %s\n", errno, strerror(errno));
        return EXIT_FAILURE;
    } else if(pid > 0) {
        //关闭另外一个套接字
        close(socket_pair[1]);
        int size = write(socket_pair[0], str, strlen(str));
        printf("Write success, pid: %d\n", getpid());
 
    } else if(pid == 0) {
        //关闭另外一个套接字
        close(socket_pair[0]);
        read(socket_pair[1], buf, sizeof(buf));        
        printf("Read result: %s, pid: %d\n",buf, getpid());
    }
 
    for(;;) {
        sleep(1);
    }
 
    return EXIT_SUCCESS;    
} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74